package com.liujie.websocket;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.CrossOrigin;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @ServerEndpoint 注解的作用
 * 这个注解用于标识作用在类上，它的主要功能是把当前类标识成一个WebSocket的服务端
 * 注解的值用户客户端连接访问的URL地址
 */
@Slf4j
@Component
@ServerEndpoint("/websocket/{roomId}")
@CrossOrigin(origins = "*",maxAge = 3600)//解决跨域，时间1小时
public class WebSocketServer {
    //房间号
    private String roomId;
    //与某个客户端的连接对话，需要通过它来给客户端发送消息
    private Session session;
    //标识当前连接客户端的用户id
    private String userId = "";
    //同一房间号的客户端session存入一个key里面
    private static ConcurrentHashMap<String, Set<Session>> rooms = new ConcurrentHashMap<>();
    //用于存所有的连接服务的客户端的用户id和session
    private static ConcurrentHashMap<String, Session> users = new ConcurrentHashMap<>();
    //用于存每个房间的所有画布
    private static ConcurrentHashMap<String, Map<Integer, String>> canvas = new ConcurrentHashMap<>();

    //连接建立成功调用的方法
    @OnOpen
    public void onOpen(Session session, @PathParam("roomId") String roomId) {
        this.roomId = roomId;
        this.session = session;
        this.userId = String.valueOf(session.getBasicRemote());
        users.put(userId, session);
        if (!rooms.contains(roomId)) {
            Set<Session> s = new HashSet<>();
            s.add(session);
            rooms.put(roomId, s);
        }else {
            rooms.get(roomId).add(session);
        }
        log.info("用户连接:" + userId + "，房间号"+roomId+"的在线人数为:" + rooms.get(roomId).size());
        try {
            sendMessage("连接成功！");
        } catch (IOException e) {
            log.error("用户:" + userId + ",网络异常!!!!!!");
        }
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        users.remove(userId);
        rooms.get(roomId).remove(session);
        log.info("用户退出:" + userId + "，房间号"+roomId+"的在线人数为:" + rooms.get(roomId).size());
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的数据
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        //log.info("用户消息:" + userId + ",报文:" + message);
        //存储白板信息
        /*int num = message.charAt(0);//共享白板的页码
        String info=message.substring(1);//共享白板的信息
        if (!canvas.contains(roomId)){
            Map<Integer, String> canve = new HashMap<>();
            canve.put(num, info);
            canvas.put(roomId, canve);
        }
        canvas.get(roomId).put(num,info);*/
        Set<Session> sessions = rooms.get(roomId);
        sessions.remove(session);//去除操作人
        try {
            for (Session s : sessions) {
                sendMessage(message, s);
                log.info("转发成功！--->"+session.getBasicRemote());
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    /**
     * 发生错误时调用
     *
     * @param error
     */
    @OnError
    public void onError(Throwable error) {
        log.error("用户错误:" + this.userId + ",原因:" + error.getMessage());
        error.printStackTrace();
    }

    /**
     * 实现服务器主动推送
     * 向当前用户发送信息
     */
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }

    /**
     * 向指定用户发送信息
     *
     * @param message
     * @param session
     * @throws IOException
     */
    public void sendMessage(String message, Session session) throws IOException {
        session.getBasicRemote().sendText(message);
    }

    /**
     * 返回房间人数
     *
     * @param roomId 房间号
     * @return 人数
     */
    public static Integer roomNum(String roomId) {
        Set<Session> sessions = rooms.get(roomId);
        if (sessions == null) {
            return 0;
        }
        return sessions.size();
    }

    /**
     * 删除房间
     *
     * @param roomId 房间号
     * @return 结果
     */
    public static boolean deleteRoom(String roomId) {
        if (rooms.contains(roomId)) {
            rooms.remove(roomId);
            return true;
        }
        return false;
    }
}
